﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using System.Windows.Threading;
using IronCow;
using Milkify.ComponentModel;
using System.Collections.Specialized;

namespace Milkify.Data
{
    [TypeConverter(typeof(ComponentModel.TaskListSortTypeConverter))]
    public enum TaskListSort
    {
        Priority,
        Name,
        Due,
        TimeEstimate
    }

    public enum TaskListFilter
    {
        Incomplete,
        Completed,
        Deleted,
        All
    }

    [System.Diagnostics.DebuggerDisplay("TaskListViewData [{ListName}]")]
    public class TaskListViewData : INotifyPropertyChanged
    {
        private SuspendableObservableCollection<Task> mTasks;

        public string ListName { get; private set; }
        public string SearchQuery { get; private set; }
        public TaskList TaskList { get; private set; }
        public IEnumerable<Task> Tasks { get { return mTasks; } }
        public int IncompleteTasksCount { get; private set; }
        public int CompletedTasksCount { get; private set; }
        public int TotalTasksCount { get; private set; }

        public ICommand NavigateToTaskCommand { get; set; }
        public ICommand CompleteTasksCommand { get; set; }
        public ICommand EditTasksCommand { get; set; }
        public ICommand PostponeTasksCommand { get; set; }
        public ICommand DeleteTasksCommand { get; set; }
        public ICommand UncompleteTasksCommand { get; set; }

        private bool mIsLoading = false;
        public bool IsLoading
        {
            get { return mIsLoading; }
            private set
            {
                if (mIsLoading != value)
                {
                    mIsLoading = value;
                    OnPropertyChanged("IsLoading");
                }
            }
        }

        public bool IsLoaded { get; private set; }

        public TaskListViewData(TaskList list)
        {
            if (list == null)
                throw new ArgumentNullException("list");

            ListName = list.Name;
            TaskList = list;
            mTasks = new SuspendableObservableCollection<Task>();
            mTasks.CollectionChanged += new NotifyCollectionChangedEventHandler(Tasks_CollectionChanged);
            IsLoaded = false;
        }

        public TaskListViewData(string listName, string searchQuery)
        {
            ListName = listName;
            SearchQuery = searchQuery;
            mTasks = new SuspendableObservableCollection<Task>();
            mTasks.CollectionChanged += new NotifyCollectionChangedEventHandler(Tasks_CollectionChanged);
            IsLoaded = false;
        }

        private void Tasks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
        }

        public void PreLoadTasks(IEnumerable<Task> tasks)
        {
            SetTasksAndEndLoading(tasks);
        }

        public void RefreshAsync(Rtm rtm)
        {
            IsLoading = true;
            var func = new Func<Rtm, IEnumerable<Task>>(GetRequestedTasks);
            func.BeginInvoke(rtm, RefreshAsyncCallback, func);
        }

        private void RefreshAsyncCallback(IAsyncResult ar)
        {
            IEnumerable<Task> tasks = null;
            bool isOnUIThread = (App.Current == null || App.Current.Dispatcher.CheckAccess());

            try
            {
                var func = (Func<Rtm, IEnumerable<Task>>)ar.AsyncState;
                tasks = func.EndInvoke(ar);
            }
            catch (Exception e)
            {
                var outerException = new Exception("An error occured while retrieving the tasks from RTM.", e);
                if (isOnUIThread)
                {
                    throw outerException;
                }
                else
                {
                    // Raise this exception on the UI thread so it's picked up
                    // by the application exception handler.
                    App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() => { throw outerException; }));
                }
            }

            if (tasks == null)
                return;

            try
            {
                if (isOnUIThread)
                {
                    SetTasksAndEndLoading(tasks);
                }
                else
                {
                    App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action<IEnumerable<Task>>)SetTasksAndEndLoading, tasks);
                }
            }
            catch(Exception e)
            {
                if (isOnUIThread)
                {
                    throw e;
                }
                else
                {
                    // Raise this exception on the UI thread so it's picked up
                    // by the application exception handler.
                    App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() => { throw e; }));
                }
            }
        }

        private IEnumerable<Task> GetRequestedTasks(Rtm rtm)
        {
            if (TaskList != null)
            {
                return TaskList.Tasks;
            }
            else if (!string.IsNullOrEmpty(SearchQuery))
            {
                return rtm.GetTasks(null, SearchQuery, SearchMode.LocalAndRemote);
            }
            else
            {
                return null;
            }
        }

        private void SetTasks(IEnumerable<Task> tasks)
        {
            lock (this)
            {
                mTasks.SuspendNotifications();

                try
                {
                    mTasks.Clear();

                    IncompleteTasksCount = 0;
                    CompletedTasksCount = 0;
                    if (tasks != null)
                    {
                        foreach (var task in tasks)
                        {
                            if (task.IsCompleted)
                                CompletedTasksCount++;
                            if (task.IsIncomplete)
                                IncompleteTasksCount++;

                            mTasks.Add(task);
                        }
                    }
                    TotalTasksCount = mTasks.Count;
                }
                finally
                {
                    mTasks.ResumeNotifications(true);
                }

                OnPropertyChanged("IncompleteTasksCount");
                OnPropertyChanged("CompletedTasksCount");
                OnPropertyChanged("TotalTasksCount");
            }
        }

        private void SetTasksAndEndLoading(IEnumerable<Task> tasks)
        {
            SetTasks(tasks);
            IsLoading = false;
            IsLoaded = true;
            CommandManager.InvalidateRequerySuggested();
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion
    }
}
